package rmiClient;

import java.io.IOException;
import java.io.Serializable;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Scanner;

import domain.Bill;

import exceptions.NegativeValueException;
import exceptions.NoExistingPriceStepException;
import exceptions.OverlappingPriceRangeException;

import server.BillingSecureInterface;
import server.BillingServerInterface;
import server.SubscriptionInterface;
import service.Event;

public class ManagementClient implements ManagementClientCallback, Serializable {


	private SubscriptionInterface analyticsServer;
	private BillingServerInterface billingServer;
	private BillingSecureInterface billingServerSecure;
	private boolean isActive = true;
	private boolean auto = true;
	private BufferHolder bufferHolder = new BufferHolder();
	private ManagementClientCallback callbackClient;
	private int port=0;
	private String host;
	private String username;
	private String analyticsName;
	private String billingName;

	public static void main (String [] args) {


		ManagementClient mc = new ManagementClient(args);
	}

	public ManagementClient(String[] args) {
		if(args.length!=2){
			usage();
		}
		checkArgs(args);
		
		
		try {
			Properties props = new Properties();
			java.io.InputStream is = ClassLoader.getSystemResourceAsStream("./registry.properties");
			props.load(is);

			if (is != null) {
				try {			
					

					port = Integer.parseInt(props.getProperty("registry.port"));
					host = props.getProperty("registry.host");
					
				} catch (NumberFormatException e) {
					e.printStackTrace();
				} finally {
					try {
						is.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			} else {
				System.err.println("Properties file not found!");
			}
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		

		try {
			
			Registry registry = LocateRegistry.getRegistry(host,port); //TODO argument handling
			billingServer = (BillingServerInterface) registry.lookup(billingName);
			analyticsServer = (SubscriptionInterface) registry.lookup(analyticsName);
			callbackClient = (ManagementClientCallback) UnicastRemoteObject.exportObject(this,0);

			run();
		} catch (RemoteException e) {
			System.out.println("RMI Components unavailable. Shutting down...");
			shutDown();
		} catch (NotBoundException e) {
			System.out.println("RMI Components unavailable. Shutting down...");
			shutDown();
		}
		
		
	}

	private void checkArgs(String[] args) {
		analyticsName = args[0];
		billingName = args[1];
	}

	private void run() throws RemoteException {
		Scanner stdin =  new Scanner(System.in);
		String userinput;

		while (isActive) {
			userinput=stdin.nextLine().trim();
			validateInput(userinput);

		}
		stdin.close();
	}

	public void validateInput(String input) throws RemoteException {
		if(input.startsWith("!login ")) {
			String[] logindata = input.split(" ");
			if(logindata.length!=3) {
				commandUsage();
				return;
			}
			billingServerSecure = (BillingSecureInterface) billingServer.login(logindata[1], logindata[2]);
			this.username = logindata[1];
			if(billingServerSecure==null) System.out.println("ERROR: Wrong username or password");
			else System.out.println(username+ " successfully logged in");
			return;
		} else if(input.equals("!steps")) {
			if(billingServerSecure == null) {
				System.out.println("ERROR: You are currently not logged in.");
			} else {
				System.out.println(billingServerSecure.getPriceSteps().toString());
			}
			return;
		} else if(input.startsWith("!addStep ")) {
			String[] stepdata = input.split(" ");
			if(stepdata.length!=5) {
				commandUsage();
				return;
			}
			if(billingServerSecure == null) {
				System.out.println("ERROR: You are currently not logged in.");
			} else {
				double startPrice;
				double endPrice;
				double fixedPrice;
				double variablePricePercent;

				startPrice = checkDouble(stepdata[1]);
				endPrice = checkDouble(stepdata[2]);
				fixedPrice = checkDouble(stepdata[3]);
				variablePricePercent = checkDouble(stepdata[4]);

				try {
					billingServerSecure.createPriceStep(startPrice, endPrice, fixedPrice, variablePricePercent);
				} catch (NegativeValueException e) {
					System.out.println(e.getMessage());
					return;
				} catch(OverlappingPriceRangeException e) {
					System.out.println(e.getMessage());
					return;
				}
				
				String endPriceString = ""+endPrice;
				if(endPrice == 0) endPriceString = "INFINITY";
				System.out.println("Step ["+startPrice+" "+endPriceString+"] successfully added");
			}
			return;

		} else if(input.startsWith("!removeStep ")) {
			
			String[] stepdata = input.split(" ");
			if(stepdata.length!=3) {
				commandUsage();
				return;
			}
			if(billingServerSecure == null) {
				System.out.println("ERROR: You are currently not logged in.");
				return;
			} else {
				double startPrice;
				double endPrice;
				
				startPrice = checkDouble(stepdata[1]);
				endPrice = checkDouble(stepdata[2]);
				
				try {
					billingServerSecure.deletePriceStep(startPrice, endPrice);
				} catch (NoExistingPriceStepException e) {
					System.out.println(e.getMessage());
				}
			}
			
			
		} else if(input.startsWith("!bill ")) {
			String[] billdata = input.split(" ");
			if(billdata.length!=2) {
				commandUsage();
				return;
			}
			if(billingServerSecure==null) {
				System.out.println("ERROR: You are currently not logged in.");
			} else {
				Bill bill = billingServerSecure.getBill(billdata[1]);
				if(bill == null) {
					System.out.println("No bill exists for "+billdata[1]);
				} else {
					System.out.println(bill.toString());
				}
			}

		} else if(input.equals("!logout")) {
			if(billingServerSecure==null) {
				System.out.println("ERROR: You are currently not logged in.");
			} else {
				billingServerSecure = null;
				System.out.println(this.username+"successfully logged out");
				this.username = null;
			}

		} else if(input.startsWith("!subscribe ")) {
			String[] subdata = input.split(" ");
			if(subdata.length!=2) {
				commandUsage();
				return;
			}
			String id = analyticsServer.subscribe(subdata[1], callbackClient);
			System.out.println("Created subscription with ID "+id+" for events using filter "+subdata[1]);
			return;
		} else if(input.startsWith("!unsubscribe ")) {
			String[] unsubdata = input.split(" ");
			if(unsubdata.length!=2) {
				commandUsage();
				return;
			}
			int id = analyticsServer.unsubscribe(unsubdata[1]);
			if(id == -1) {
				System.out.println("Invalid subscription id.");
			} else {
				System.out.println("subscription "+id+" terminated");
			}
			return;
		} else if(input.startsWith("!auto")) {
			this.auto = true;
		} else if(input.startsWith("!hide")) {
			this.auto = false;
		} else if(input.startsWith("!print")) {
			boolean hasNext = true;
			do {
				Event e = bufferHolder.get();
				if(e != null) printEvent(e);
				else hasNext = false;
			} while(hasNext);
		} else if(input.equals("!exit")) {
			shutDown();
		} else {
			commandUsage();
		}
	}

	private void shutDown() {
		isActive = false;
		try {
			UnicastRemoteObject.unexportObject(this, true);
		} catch (NoSuchObjectException e) {
			// TODO Auto-generated catch block
		}
		System.exit(0);
		
	}

	private void commandUsage() {
		System.out.println("You can use the following commands: \n" +
				"!subscribe <subscriptionfilter> \n" +
				"!unsubscribe <subscription-id> \n" +
				"!auto \n" +
				"!hide \n" +
				"!print \n" +
				"!login <username> <password> \n" +
				"!logout \n" +
				"!steps \n" +
				"!addStep <start> <end> <fix> <variable> \n" +
				"!removeStep <start> <end> \n" +
				"!bill <username>");
		
		
	}

	private double checkDouble(String string) {
		double d=-1;
		try{
			d=Double.parseDouble(string);
		} catch(NumberFormatException e) {
			e.printStackTrace();
		}
		return d;
	}

	private void usage() {
		System.out.println("Usage: java ManagementClient <analytics server binding name> <billing server binding name>\nExample: java ManagementClient analyticsServer billingServer");
		System.exit(0);
	}

	private void printEvent(Event e) {
		//TODO print event details
		System.out.println(e.toString());
	}


	@Override
	public void processEvent(Event e) throws RemoteException {
		if(auto == true) printEvent(e);
		else bufferHolder.insert(e);

	}
}

class BufferHolder implements Serializable {


	private LinkedList<Event> buffer = new LinkedList<Event>();


	public void insert(Event e) {
		synchronized(this) {
			buffer.offer(e);
		}
	}

	public Event get() {
		Event e;
		synchronized(this) {
			e = buffer.remove();
		}
		return e;
	}

}